/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.apache.tomcat.util.net;

import java.net.Socket;

import org.apache.tomcat.util.threads.ThreadWithAttributes;

/**
 * Regular master slave thread pool. Slave threads will wait for work.
 */
class MasterSlaveWorkerThread implements Runnable {

	protected PoolTcpEndpoint endpoint;
	protected String threadName;
	protected boolean stopped = false;
	private Object threadSync = new Object();
	private Thread thread = null;
	private boolean available = false;
	private Socket socket = null;
	private TcpConnection con = new TcpConnection();
	private Object[] threadData = null;

	public MasterSlaveWorkerThread(PoolTcpEndpoint endpoint, String threadName) {
		this.endpoint = endpoint;
		this.threadName = threadName;
	}

	/**
	 * Process an incoming TCP/IP connection on the specified socket. Any
	 * exception that occurs during processing must be logged and swallowed.
	 * <b>NOTE</b>: This method is called from our Connector's thread. We must
	 * assign it to our own thread so that multiple simultaneous requests can be
	 * handled.
	 * 
	 * @param socket
	 *            TCP socket to process
	 */
	synchronized void assign(Socket socket) {

		// Wait for the Processor to get the previous Socket
		while (available) {
			try {
				wait();
			} catch (InterruptedException e) {
			}
		}

		// Store the newly available Socket and notify our thread
		this.socket = socket;
		available = true;
		notifyAll();

	}

	/**
	 * Await a newly assigned Socket from our Connector, or <code>null</code> if
	 * we are supposed to shut down.
	 */
	private synchronized Socket await() {

		// Wait for the Connector to provide a new Socket
		while (!available) {
			try {
				wait();
			} catch (InterruptedException e) {
			}
		}

		// Notify the Connector that we have received this Socket
		Socket socket = this.socket;
		available = false;
		notifyAll();

		return (socket);

	}

	/**
	 * The background thread that listens for incoming TCP/IP connections and
	 * hands them off to an appropriate processor.
	 */
	public void run() {

		// Process requests until we receive a shutdown signal
		while (!stopped) {

			// Wait for the next socket to be assigned
			Socket socket = await();
			if (socket == null)
				continue;

			// Process the request from this socket
			endpoint.processSocket(socket, con, threadData);

			// Finish up this request
			endpoint.recycleWorkerThread(this);

		}

		// Tell threadStop() we have shut ourselves down successfully
		synchronized (threadSync) {
			threadSync.notifyAll();
		}

	}

	/**
	 * Start the background processing thread.
	 */
	public void start() {
		threadData = endpoint.getConnectionHandler().init();
		thread = new ThreadWithAttributes(null, this);
		thread.setName(threadName);
		thread.setDaemon(true);
		thread.start();
	}

	/**
	 * Stop the background processing thread.
	 */
	public void stop() {
		stopped = true;
		assign(null);
		thread = null;
		threadData = null;
	}

}
